package chat.asynch;

import javax.servlet.AsyncContext;
import javax.servlet.AsyncEvent;
import javax.servlet.AsyncListener;
import java.io.IOException;
import java.util.concurrent.*;

public class AsyncRequestController {
    private static volatile boolean isRunning;
    private static ExecutorService executorService;
    private static BlockingQueue<AsyncContextContainer> requestsQueue;

    public static synchronized void start() {
        init();
        beginDispatching();
    }

    public static synchronized void stop() {
        isRunning = false;
        requestsQueue.add(new AsyncContextContainer(null));
        cleanUp();    
    }

    public static void process(AsyncContext asyncContext) {
        requestsQueue.add(new AsyncContextContainer(asyncContext));
    }


    private static void init() {
        isRunning = true;
        executorService = Executors.newCachedThreadPool();
        requestsQueue = new LinkedBlockingQueue<AsyncContextContainer>();
    }

    private static void cleanUp() {
        executorService.shutdownNow(); 
    }

    private static void beginDispatching() {
        new Thread(
                new Runnable() {
                    public void run() {
                        System.out.println("Begin request dispatching.");
                        try {
                            while(true) {
                                AsyncContext asyncContext = requestsQueue.take().getAsyncContext();
                                if (!isRunning) {
                                    break;
                                }
                                if (asyncContext != null && !executorService.isShutdown()) {
                                    Future<?> future = executorService.submit(new AsyncRequestProcessor(asyncContext));
                                    asyncContext.addListener(new AsyncContextListener(future));
                                }
                            }
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                        System.out.println("Stop request dispatching.");
                    }
                }
        ).start();
    }


    private static class AsyncContextListener implements AsyncListener {
        private Future<?> future;

        private AsyncContextListener(Future<?> future) {
            this.future = future;
        }

        public void onTimeout(AsyncEvent asyncEvent) throws IOException {
            future.cancel(true);
        }

        public void onComplete(AsyncEvent asyncEvent) throws IOException {}

        public void onError(AsyncEvent asyncEvent) throws IOException {}

        public void onStartAsync(AsyncEvent asyncEvent) throws IOException {}
    }
}
